﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using ServoDrivesLib.Model;
using ServoDrivesLibAbstract;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Reflection;

namespace ServoDrivesLib.Controls
{
    public class LeisaiControlCard : CardControl
    {
        private readonly ushort CardId = 0; // 卡片ID
        private readonly DeviceInfo _device; // 设备信息
        private readonly string iniPath;
        private readonly string eniPath;
        private readonly string esiPath;

        private readonly ILogger<LeisaiControlCard>? _logger; // 日志记录器

        public LeisaiControlCard(ILogger<LeisaiControlCard>? logger, IConfiguration? configuration)
        {
            _logger = logger;
            iniPath = configuration?.GetRequiredSection("Leisai:Ini")?.Value;
            eniPath = configuration?.GetRequiredSection("Leisai:Eni")?.Value;
            esiPath = configuration?.GetRequiredSection("Leisai:Esi")?.Value;
            this.CopyDll("LTDMC.dll");
            this.LogMessage("");
        }

        public override IEtherCATMode EtherCATControl => this; // 返回EtherCAT模式接口
        public override IServoControl ServoControl => this; // 返回伺服控制接口

        internal override ILogger? Logger { get => _logger; }


        // 设置轴的位置
        public override int ClearPosition(int axis)
        {
            double dPos = 0;
            var ret = LTDMC.dmc_set_position_unit(CardId, (ushort)axis, dPos);
            this.LogMessage(ret.ToString());
            return ret;
        }

        // 关闭设备
        public override int Close()
        {
            var ret = LTDMC.dmc_board_close();
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 下载内存文件
        public override int DownloadMemFile()
        {
            int ret = 0;
            try
            {
                using (var client = File.OpenRead(eniPath))
                {
                    using (var sr = new StreamReader(client, Encoding.UTF8))
                    {
                        byte[] data = Encoding.UTF8.GetBytes(sr.ReadToEnd());
                        ret = LTDMC.dmc_download_memfile(this.CardId, data, (uint)data.Length, null, 200);
                    }
                }
                this.LogMessage(ret.ToString());

                using (var client = File.OpenRead(iniPath))
                {
                    using (var sr = new StreamReader(client, Encoding.UTF8))
                    {
                        byte[] data = Encoding.UTF8.GetBytes(sr.ReadToEnd());
                        ret = LTDMC.dmc_download_memfile(this.CardId, data, (uint)data.Length, null, 201);
                    }
                }
                this.LogMessage(ret.ToString());
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex.ToString());
            }

            return ret;
        }

        // 结束扫描
        public override int EndScan()
        {
            ushort status = 0;
            var ret = LTDMC.nmc_stop_etc(CardId, ref status);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 获取轴的数量
        public override int GetAxisNums(ref int axisNums)
        {
            uint i = 0;
            var ret = LTDMC.nmc_get_total_axes(CardId, ref i);
            axisNums = (int)i;
            this.LogMessage(ret.ToString());

            return ret;
        }

        public override int GetAxisType(int axis, ref int type)
        {
            ushort i = 0;
            var ret = LTDMC.nmc_get_axis_type(CardId, (ushort)axis, ref i);
            type = (int)i;
            this.LogMessage(ret.ToString());
            return ret;
        }

        // 获取编码器单位
        public override int GetEncoderUnit(int axis, ref double encoder)
        {
            var ret = LTDMC.dmc_get_encoder_unit(CardId, (ushort)axis, ref encoder);

            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 获取等效值
        public override int GetEquiv(int axis, ref double equip)
        {
            var ret = LTDMC.dmc_get_equiv(CardId, (ushort)axis, ref equip);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 获取EtherCAT消耗时间
        public override int GetEtherCATConsumeTime(ref ulong averageTime, ref ulong maxTime, ref ulong cycles)
        {
            uint avg = 0, max = 0;
            var ret = LTDMC.nmc_get_consume_time_fieldbus(CardId, 2, ref avg, ref max, ref cycles);
            averageTime = avg;
            maxTime = max;

            this.LogMessage(ret.ToString());

            return ret;
        }

        // 获取归零移动状态
        public override int GetHomeMove(int axis, ref int status)
        {
            ushort s = 0;
            var ret = LTDMC.dmc_get_home_result(CardId, (ushort)axis, ref s);
            status = s;
            this.LogMessage(ret.ToString());

            return ret;
        }

        // 获取速度曲线参数
        public override int GetProFile(int axis, ref double minSpeed, ref double maxSpeed, ref double tAcc, ref double tDec,
            ref double stopSpeed, ref double sramp)
        {
            var ret = LTDMC.dmc_get_s_profile(CardId, (ushort)axis, 0, ref sramp);
            this.LogMessage($"dmc_get_s_profile = {ret}");

            ret = LTDMC.dmc_get_profile_unit(CardId, (ushort)axis, ref minSpeed, ref maxSpeed, ref tAcc, ref tDec,
                ref stopSpeed);
            this.LogMessage($"dmc_get_profile_unit = {ret}");
            return ret;
        }

        // 获取从站数量
        public override int GetSlaveValue(ref int slaveValue)
        {
            ushort s = 0;
            var ret = LTDMC.nmc_get_total_slaves(CardId, 2, ref s);
            slaveValue = s;
            this.LogMessage(ret.ToString());

            return ret;
        }

        // 初始化设备
        public override int Init()
        {
            var ret = LTDMC.dmc_board_init();

            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 绝对位置移动
        public override int MoveAxis(int axis, double position)
        {
            var ret = LTDMC.dmc_pmove_unit(CardId, (ushort)axis, position, 0);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 相对位置移动
        public override int MoveAxisAbs(int axis, double position)
        {
            var ret = LTDMC.dmc_pmove_unit(CardId, (ushort)axis, position, 1);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 停止轴运动
        public override int MoveStop(int axis)
        {
            _logger?.LogInformation("axis={axis}", axis); // 记录日志
            var ret = LTDMC.dmc_stop(this.CardId, (ushort)axis, 1);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        [Obsolete("雷赛板卡不支持这样使用PDO")]
        // 读取PDO数据
        public override int PDORead(int slave, int index, int subIndex, int valueLength, ref int value)
        {
            int ret = 0;
            slave += 1000;
            byte[] data = new byte[4];
            ret = LTDMC.nmc_read_rxpdo(CardId, 2, (ushort)slave, (ushort)index, (ushort)subIndex,
                (ushort)valueLength, data);

            value = data.BytesToInt32();

            this.LogMessage(ret.ToString());
            return ret;
        }
        [Obsolete("雷赛板卡不支持这样使用PDO")]
        // 写入PDO数据
        public override int PDOWrite(int slave, int index, int subIndex, int valueLength, int value)
        {
            int ret = 0;
            slave += 1000;

            byte[] data = value.Int32ToBytes();
            ret = LTDMC.nmc_write_rxpdo(CardId, 2, (ushort)slave, (ushort)index, (ushort)subIndex,
                (ushort)valueLength, data);

            this.LogMessage(ret.ToString());
            return ret;
        }

        // 复位卡
        public override int Rest(CardResetStatus status = CardResetStatus.Software)
        {
            _logger?.LogInformation("status={status}", status); // 记录日志
            int ret = 0;
            switch (status)
            {
                case CardResetStatus.Cool:
                    ret = LTDMC.dmc_cool_reset(this.CardId);
                    break;
                case CardResetStatus.Software:
                    ret = LTDMC.dmc_soft_reset(this.CardId);
                    break;
                case CardResetStatus.Origin:
                    ret = LTDMC.dmc_original_reset(this.CardId);
                    break;
            }
            this.LogMessage(ret.ToString());

            return ret;
        }

        // 读取SDO数据
        public override int SDORead(int slave, int index, int subIndex, int valueLength, ref int value)
        {
            slave = 1000 + slave;
            var ret = LTDMC.nmc_get_node_od(CardId, 2, (ushort)slave, (ushort)index, (ushort)subIndex,
                (ushort)valueLength,
                ref value);

            this.LogMessage(ret.ToString());


            return ret;
        }

        // 写入SDO数据
        public override int SDOWrite(int slave, int index, int subIndex, int valueLength, int value)
        {
            slave = 1000 + slave;
            var ret = LTDMC.nmc_set_node_od(CardId, 2, (ushort)slave, (ushort)index, (ushort)subIndex,
                (ushort)valueLength,
                value);
            this.LogMessage(ret.ToString());


            return ret;
        }

        // 禁用轴
        public override int SetAxisDisable(int axis)
        {
            var ret = LTDMC.nmc_set_axis_disable(this.CardId, (ushort)axis);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 启用轴
        public override int SetAxisEnable(int axis)
        {
            var ret = LTDMC.nmc_set_axis_enable(this.CardId, (ushort)axis);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 设置等效值
        public override int SetEquiv(int axis, double equip)
        {
            var ret = LTDMC.dmc_set_equiv(CardId, (ushort)axis, equip);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 开始归零移动
        public override int SetHomeMove(int axis)
        {
            var ret = LTDMC.nmc_home_move(CardId, (ushort)axis);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 设置速度曲线参数
        public override int SetProFile(int axis, double minSpeed, double maxSpeed, double tAcc, double tDec, double stopSpeed,
            double sramp)
        {
            var ret = LTDMC.dmc_set_s_profile(CardId, (ushort)axis, 0, sramp);
            this.LogMessage($"dmc_set_s_profile = {0}", ret.ToString());

            ret = LTDMC.dmc_set_profile_unit(CardId, (ushort)axis, minSpeed, maxSpeed, tAcc, tDec, stopSpeed);
            this.LogMessage($"dmc_set_s_profile = {0}", ret.ToString());
            return ret;
        }

        // 开始扫描
        public override int StartScan()
        {
            var ret = LTDMC.nmc_start_scan_ethercat(CardId, 2);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        // 停止EtherCAT
        public override int StopEtherCAT()
        {
            ushort status = 0;
            _logger?.LogInformation(" ");
            var ret = LTDMC.nmc_stop_etc(CardId, ref status);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        public override int GetAxisStatus(int axis, ref int status)
        {
            ushort s = 0;
            var ret = LTDMC.nmc_get_axis_state_machine(CardId, (ushort)axis, ref s);
            status = s;
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetAxisSpeed(int axis, ref double speed)
        {
            var ret = LTDMC.dmc_read_current_speed_unit(CardId, (ushort)axis, ref speed);
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetAxisMoveStatus(int axis)
        {
            var ret = LTDMC.dmc_check_done(CardId, (ushort)axis);
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int GetAxisError(int axis, ref int error)
        {
            ushort tmp = 0;
            var ret = LTDMC.nmc_get_axis_errcode(CardId, (ushort)axis, ref tmp);
            error = tmp;
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int ClearAxisError(int axis)
        {
            var ret = LTDMC.nmc_clear_axis_errcode(CardId, (ushort)axis);
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int SetCycleTime(int cycleTime)
        {
            var ret = LTDMC.nmc_set_cycletime(CardId, 2, cycleTime);
            this.LogMessage(ret.ToString());
            return (int)ret;
        }

        public override int GetErrorCode(ref int code)
        {
            ushort tmp = 0;
            var ret = LTDMC.nmc_get_errcode(this.CardId, 2, ref tmp);
            this.LogMessage(ret.ToString());
            return ret;
        }

        public override int ClearErrorCode()
        {
            var ret = LTDMC.nmc_clear_errcode(CardId, 2);
            this.LogMessage(ret.ToString());
            return ret;
        }
    }


    internal static class Int32Ex
    {
        public static byte[] Int32ToBytes(this int data)
        {
            return BitConverter.GetBytes(data);
        }

        public static int BytesToInt32(this byte[] data, bool reverse = true)
        {
            if (reverse)
            {
                Array.Reverse(data);
            }

            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0);
            Union union = Marshal.PtrToStructure<Union>(ptr);
            return union.I32;
        }
    }
}